Aidemy ディープラーニング基礎
深層学習の実装
データの用意
手書き数字のデータセットにはMNISTというデータセットを用いる。
MNISTには、膨大な数の手書き数字画像とそれぞれの画像に対し「0~9」で示された正解ラベルが含まれている。
MNISTはYann LeCun's websiteで公開されているが、Kerasでコードを実行することで比較的簡単にローカル(ご自身のPC)にダウンロードできる。
Xが大量の画像データ、yが大量の教師ラベルのデータを意味する。trainはモデルの学習用のデータ、testはモデルの性能を評価する際に使うデータ。ただしtrainデータとtestデータには、データとして本質的な違いはない。
code: Python
from keras.datasets import mnist
(X_train, y_train), (X_test, y_test) = mnist.load_data()
print(X_train.shape, y_train.shape, X_test.shape, y_test.shape)
--------------------------------------------------------------------------
(60000, 28, 28) (60000,) (10000, 28, 28) (10000,)
--------------------------------------------------------------------------
モデルの生成
code: Python
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Activation
from keras.utils.vis_utils import plot_model
from keras.utils.np_utils import to_categorical
import matplotlib.pyplot as plt
(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train = X_train.reshape(X_train.shape0, 784):6000 X_test = X_test.reshape(X_test.shape0, 784):1000 y_train = to_categorical(y_train):6000 y_test = to_categorical(y_test):1000 model = Sequential()
# 入力がユニット数は784, 1つ目の全結合層の出力ユニット数は256
model.add(Dense(256, input_dim=784))
model.add(Activation("sigmoid"))
# 2つ目の全結合層の出力ユニット数は128
model.add(Dense(128))
model.add(Activation("relu"))
# 3つ目の全結合層(出力層)の出力ユニット数は10
model.add(Dense(10))
model.add(Activation("softmax"))
model.compile(optimizer="sgd", loss="categorical_crossentropy", metrics="accuracy") # モデル構造の出力
plot_model(model, "model125.png", show_layer_names=False)
# モデル構造の可視化
image = plt.imread("model125.png")
plt.figure(dpi=150)
plt.imshow(image)
plt.show()
--------------------------------------------------------------------------
--------------------------------------------------------------------------
モデルの学習
code: Python
from keras.datasets import mnist
from keras.layers import Activation, Dense
from keras.models import Sequential
from keras import optimizers
from keras.utils.np_utils import to_categorical
import matplotlib.pyplot as plt
(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train = X_train.reshape(X_train.shape0, 784):6000 X_test = X_test.reshape(X_test.shape0, 784):1000 y_train = to_categorical(y_train):6000 y_test = to_categorical(y_test):1000 model = Sequential()
model.add(Dense(256, input_dim=784))
model.add(Activation("sigmoid"))
model.add(Dense(128))
model.add(Activation("sigmoid"))
model.add(Dense(10))
model.add(Activation("softmax"))
model.compile(optimizer="sgd", loss="categorical_crossentropy", metrics="accuracy") history = model.fit(X_train, y_train, verbose=1, epochs=3)
plt.plot(history.history"accuracy", label="acc", ls="-", marker="o") plt.ylabel("accuracy")
plt.xlabel("epoch")
plt.legend(loc="best")
plt.show()
--------------------------------------------------------------------------
--------------------------------------------------------------------------
モデルの評価
code: Python
import numpy as np
import matplotlib.pyplot as plt
from keras.datasets import mnist
from keras.layers import Activation, Dense
from keras.models import Sequential, load_model
from keras import optimizers
from keras.utils.np_utils import to_categorical
(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train = X_train.reshape(X_train.shape0, 784):10000 X_test = X_test.reshape(X_test.shape0, 784):1000 y_train = to_categorical(y_train):10000 y_test = to_categorical(y_test):1000 model = Sequential()
model.add(Dense(256, input_dim=784))
model.add(Activation("sigmoid"))
model.add(Dense(128))
model.add(Activation("sigmoid"))
model.add(Dense(10))
model.add(Activation("softmax"))
model.compile(optimizer="sgd", loss="categorical_crossentropy", metrics="accuracy") model.fit(X_train, y_train, verbose=1)
score = model.evaluate(X_test, y_test, verbose=1)
print("evaluate loss: {00}\nevaluate acc: {01}".format(score)) --------------------------------------------------------------------------
--------------------------------------------------------------------------
モデルによる分類
code: Python
import numpy as np
import matplotlib.pyplot as plt
from keras.datasets import mnist
from keras.layers import Activation, Dense
from keras.models import Sequential, load_model
from keras.utils.np_utils import to_categorical
(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train = X_train.reshape(X_train.shape0, 784):6000 X_test = X_test.reshape(X_test.shape0, 784):1000 y_train = to_categorical(y_train):6000 y_test = to_categorical(y_test):1000 model = Sequential()
model.add(Dense(256, input_dim=784))
model.add(Activation("sigmoid"))
model.add(Dense(128))
model.add(Activation("sigmoid"))
model.add(Dense(10))
model.add(Activation("softmax"))
model.compile(optimizer="sgd", loss="categorical_crossentropy", metrics="accuracy") model.fit(X_train, y_train, verbose=1)
score = model.evaluate(X_test, y_test, verbose=0)
print("evaluate loss: {00}\nevaluate acc: {01}".format(score)) # テストデータの最初の10枚を表示する
for i in range(10):
plt.subplot(1, 10, i+1)
plt.imshow(X_testi.reshape((28,28)), "gray") plt.show()
# X_testの最初の10枚の予測されたラベルを表示
pred = np.argmax(model.predict(X_test0:10), axis=1) print(pred)
--------------------------------------------------------------------------
--------------------------------------------------------------------------
深層学習のチューニング
ハイパーパラメータ
深層学習手法を使うと、分類あるいは回帰のアルゴリズムをほとんど自動的に生成できるためとても便利。また、ニューラルネットワークモデルはいろいろな場面に適用させることができ汎用的。
しかし、ネットワークを構成する際に人が調整するべきパラメーターがいくつか存在する。これらは ハイパーパラメータ と呼ばれる。
ネットワーク構造
ネットワークの構造(隠れ層の数、隠れ層のユニット数)は自由に決めて生成することができる。
一般に、隠れ層やユニット数を多くすると、多彩な関数が表現できるようになる。
しかし、隠れ層が多くなると、入力層に近い重みを適切に更新するのが難しく学習がなかなか進みにくくなったり、
隠れ層のユニット数が多くなると重要性の低い特徴量を抽出してしまい過学習(汎化性能が低くなった状態)をしやすくなるなど、適切にネットワークの構造を設定する必要がある。
ネットワーク構造は理論で裏付けて定めることが難しく、実際には他の似たような実装例を参考にするなど経験に基づいて決定される傾向がある。
code: Python
import numpy as np
import matplotlib.pyplot as plt
from keras.datasets import mnist
from keras.layers import Activation, Dense, Dropout
from keras.models import Sequential, load_model
from keras import optimizers
from keras.utils.np_utils import to_categorical
(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train = X_train.reshape(X_train.shape0, 784):6000 X_test = X_test.reshape(X_test.shape0, 784):1000 y_train = to_categorical(y_train):6000 y_test = to_categorical(y_test):1000 model = Sequential()
model.add(Dense(256, input_dim=784))
model.add(Activation("sigmoid"))
def funcA():
model.add(Dense(128))
model.add(Activation("sigmoid"))
def funcB():
model.add(Dense(128))
model.add(Activation("sigmoid"))
model.add(Dense(128))
model.add(Activation("sigmoid"))
model.add(Dense(128))
model.add(Activation("sigmoid"))
def funcC():
model.add(Dense(1568))
model.add(Activation("sigmoid"))
funcA()
# funcB()
# funcC()
model.add(Dropout(rate=0.5))
model.add(Dense(10))
model.add(Activation("softmax"))
sgd = optimizers.SGD(lr=0.1)
model.compile(optimizer=sgd, loss="categorical_crossentropy", metrics="accuracy") model.fit(X_train, y_train, batch_size=32, epochs=3, verbose=1)
score = model.evaluate(X_test, y_test, verbose=0)
print("evaluate loss: {00}\nevaluate acc: {01}".format(score)) --------------------------------------------------------------------------
--------------------------------------------------------------------------
ドロップアウト
ドロップアウト は、 過学習を防ぎモデルの精度をあげるための手法の一つ 。
ドロップアウトを使うと、ユニットの一部が学習のたびにランダムに削除(より正確には0で上書き)される。
これにより、ニューラルネットは特定のニューロンの存在に依存できなくなり、より 汎用的な(学習データ以外でも通用しやすい)特徴を学習する ようになる。 その結果、学習データに対する過学習を防ぐことができる。
code: Python
import numpy as np
import matplotlib.pyplot as plt
from keras.datasets import mnist
from keras.layers import Activation, Dense, Dropout
from keras.models import Sequential, load_model
from keras import optimizers
from keras.utils.np_utils import to_categorical
(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train = X_train.reshape(X_train.shape0, 784):6000 X_test = X_test.reshape(X_test.shape0, 784):1000 y_train = to_categorical(y_train):6000 y_test = to_categorical(y_test):1000 model = Sequential()
model.add(Dense(256, input_dim=784))
model.add(Activation("sigmoid"))
model.add(Dense(128))
model.add(Activation("sigmoid"))
model.add(Dropout(rate=0.5))
model.add(Dense(10))
model.add(Activation("softmax"))
sgd = optimizers.SGD(lr=0.1)
model.compile(optimizer=sgd, loss="categorical_crossentropy", metrics="accuracy") history = model.fit(X_train, y_train, batch_size=32, epochs=5, verbose=1, validation_data=(X_test, y_test))
plt.plot(history.history"acc", label="acc", ls="-", marker="o") plt.plot(history.history"val_acc", label="val_acc", ls="-", marker="x") plt.ylabel("accuracy")
plt.xlabel("epoch")
plt.legend(loc="best")
plt.show()
--------------------------------------------------------------------------
--------------------------------------------------------------------------
活性化関数
活性化関数 とは、主に全結合層の後に適用する関数で、もともとニューロンの発火に相当していたもの。
全結合層では、入力を線形変換したものを出力するが、 活性化関数を用いることで非線形性をもたせる 。
活性化関数を使わない場合、以下のような一本の直線で分離できない(線形分離不可能)データは分類できないことが数学的にわかっている。
非線形性をもたせることで、適切に学習が進めば線形分離不可能なモデルでも必ず分類することができる。
活性化関数もハイパーパラメータ。
シグモイド関数
活性化関数として用いられる関数の1つに シグモイド関数 というものがあり、この関数は次式で与えられる。
ReLU
もうひとつ活性化関数によく用いられるものに、ReLU(ランプ関数) というものがある。ReLUはRectified Linear Unitの略。
損失関数
学習時に、モデルの出力と教師データとの差(間違え具合)を評価する関数を損失関数(誤差関数) という。
損失関数には 二乗誤差 や クロスエントロピー誤差 などが用いられる。
この損失関数を最小化するように誤差逆伝播法という手法で各層の重みは更新される。
二乗誤差
二乗誤差 は、最小二乗法として統計学など様々な分野で用いられる誤差関数。連続値の評価に優れているため主に回帰モデルの誤差関数 として使われる。
クロスエントロピー誤差
クロスエントロピー誤差 は、 二値分類の評価に特化しているため、主に分類モデルの誤差関数 として使われる。
分類の学習において、クロスエントロピー誤差は、 0~1の2つの数の差を評価する上で合理的な関数 であると言える。
最適化関数
重みの更新は、誤差関数を各重みで微分した値を元に、更新すべき方向とどの程度更新するかを決める。
微分によって求めた値を、 学習率、エポック数、過去の重みの更新量など を踏まえてどのように重みの更新に反映するかを定めるのが最適化関数。
学習率
学習率 とは、 各層の重みを一度にどの程度変更するかを決めるハイパーパラメーターのこと。
code: Python
import numpy as np
import matplotlib.pyplot as plt
from keras.datasets import mnist
from keras.layers import Activation, Dense, Dropout
from keras.models import Sequential, load_model
from keras import optimizers
from keras.utils.np_utils import to_categorical
(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train = X_train.reshape(X_train.shape0, 784):6000 X_test = X_test.reshape(X_test.shape0, 784):1000 y_train = to_categorical(y_train):6000 y_test = to_categorical(y_test):1000 model = Sequential()
model.add(Dense(256, input_dim=784))
model.add(Activation("sigmoid"))
model.add(Dense(128))
model.add(Activation("sigmoid"))
model.add(Dropout(rate=0.5))
model.add(Dense(10))
model.add(Activation("softmax"))
def funcA():
global lr
lr = 0.01
def funcB():
global lr
lr = 0.1
def funcC():
global lr
lr = 1.0
# funcA()
funcB()
# funcC()
sgd = optimizers.SGD(lr=lr)
model.compile(optimizer=sgd, loss="categorical_crossentropy", metrics="accuracy") model.fit(X_train, y_train, batch_size=32, epochs=3, verbose=1)
score = model.evaluate(X_test, y_test, verbose=0)
print("evaluate loss: {00}\nevaluate acc: {01}".format(score)) --------------------------------------------------------------------------
--------------------------------------------------------------------------
ミニバッチ学習
モデルの学習を行う際、一度にモデルに渡す入力データの数は変えることができる。一度に渡すデータの数を、 バッチサイズ といい、これもハイパーパラメータ。
一度に複数のデータを渡した時、モデルはそれぞれのデータでの損失と損失関数の勾配(重みをどのように更新するべきか)を求めるが、重みの更新は、1回のみ、求めた勾配の平均を使って行われる。
複数のデータを用いて重みの更新を行うことで、極端に変わったデータの影響をあまり受けずに済み、また並列計算が行えるので計算時間を短縮することができる。
一方、複数のデータを用いて重みの更新を行うと、極端な重みの更新が発生しなくなり、損失関数の局所解から抜け出せなくなる恐れがある。
癖の強いデータが多い時はバッチサイズを大きくする、同じようなデータが多いときはバッチサイズを小さくする などと、 バッチサイズをうまく調整する必要がある。
バッチサイズを1とする手法をオンライン学習(確率的勾配法)
バッチサイズを全データ数とする手法をバッチ学習(最急降下法)
これらの中間となる手法をミニバッチ学習と言う。
code: Python
import numpy as np
import matplotlib.pyplot as plt
from keras.datasets import mnist
from keras.layers import Activation, Dense, Dropout
from keras.models import Sequential, load_model
from keras import optimizers
from keras.utils.np_utils import to_categorical
(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train = X_train.reshape(X_train.shape0, 784):6000 X_test = X_test.reshape(X_test.shape0, 784):1000 y_train = to_categorical(y_train):6000 y_test = to_categorical(y_test):1000 model = Sequential()
model.add(Dense(256, input_dim=784))
model.add(Activation("sigmoid"))
model.add(Dense(128))
model.add(Activation("sigmoid"))
model.add(Dropout(rate=0.5))
model.add(Dense(10))
model.add(Activation("softmax"))
sgd = optimizers.SGD(lr=0.1)
model.compile(optimizer=sgd, loss="categorical_crossentropy", metrics="accuracy") def funcA():
global batch_size
batch_size = 16
def funcB():
global batch_size
batch_size = 32
def funcC():
global batch_size
batch_size = 64
# batch_size: 16
# funcA()
# batch_size: 32
# funcB()
funcC()
model.fit(X_train, y_train, batch_size=batch_size, epochs=3, verbose=1)
score = model.evaluate(X_test, y_test, verbose=0)
print("evaluate loss: {00}\nevaluate acc: {01}".format(score)) --------------------------------------------------------------------------
--------------------------------------------------------------------------
反復学習
一般に、モデルの精度をあげるため同じ訓練データを使って何度か学習させるということを行います。これを 反復学習という。
この学習を行う回数を エポック数 といい、これもハイパーパラメータ。
エポック数は大きくすればモデルの精度が上がり続ける、というものではない。
正解率は途中から伸びなくなるだけでなく、繰り返し学習をすることで損失関数を最小化させようとして過学習が起こる。
適切なタイミングで学習を打ち切ることが必要となってくる。
code: Python
import numpy as np
import matplotlib.pyplot as plt
from keras.datasets import mnist
from keras.layers import Activation, Dense, Dropout
from keras.models import Sequential, load_model
from keras import optimizers
from keras.utils.np_utils import to_categorical
(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train = X_train.reshape(X_train.shape0, 784):1500 X_test = X_test.reshape(X_test.shape0, 784):6000 y_train = to_categorical(y_train):1500 y_test = to_categorical(y_test):6000 model = Sequential()
model.add(Dense(256, input_dim=784))
model.add(Activation("sigmoid"))
model.add(Dense(128))
model.add(Activation("sigmoid"))
# 今回はDropoutを使いません。
model.add(Dense(10))
model.add(Activation("softmax"))
sgd = optimizers.SGD(lr=0.1)
model.compile(optimizer=sgd, loss="categorical_crossentropy", metrics="accuracy") def funcA():
global epochs
epochs = 5
def funcB():
global epochs
epochs = 10
def funcC():
global epochs
epochs = 60
# epochs: 5
# funcA()
# epochs: 10
funcB()
# epochs: 60
# funcC()
history = model.fit(X_train, y_train, batch_size=32, epochs=epochs, verbose=1, validation_data=(X_test, y_test))
plt.plot(history.history"acc", label="acc", ls="-", marker="o") plt.plot(history.history"val_acc", label="val_acc", ls="-", marker="x") plt.ylabel("accuracy")
plt.xlabel("epoch")
plt.legend(loc="best")
plt.show()
score = model.evaluate(X_test, y_test, verbose=0)
print("evaluate loss: {00}\nevaluate acc: {01}".format(score)) --------------------------------------------------------------------------
--------------------------------------------------------------------------